//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "Graphics/Resources/VectorFont.h"
#include "Graphics/Resources/Font.inl.h"
#include "Graphics/Loaders/FontLoader.h"
#include "Graphics/Systems/GraphicsEngine.h"

namespace Engine
{
namespace Graphics
{
	
	struct VectorFont::GlyphReader
	{
	// variables
	private:
		VectorFont &			fnt;
		
		mutable Ptr< Texture >	currTex;
		mutable uint			height;
		mutable bool			materialChanged;

		float					k;

	
	// methods
	public:
		GlyphReader (VectorFont & fnt, float k) : fnt(fnt), height(0), k(k), materialChanged(false)
		{}

		void ScaleChanged (const vec2 &scale) const
		{
			height = fnt._CalcHeight( k, scale.y );
		}

		float GetBaseLineTopMargin () const
		{
			float bltm = fnt._CalcBaseLineTopMargin( height );
			return 0.66666669f;
		}

		bool Load (uint8 i) const
		{
			Ptr< Texture >	tex = currTex;

			bool	res = fnt._LoadGlyph( i, height, tex.ref() );

			if ( tex != currTex )
			{
				materialChanged = true;
				currTex = tex;
			}
			return res;
		}

		void SetMaterial (const BatchRenderPtr &br, BatchMaterial mtr) const
		{
			if ( materialChanged )
			{
				materialChanged = false;

				mtr.ResetTextures();
				mtr.SetTexture( 0, currTex.ptr() );

				br->SetMaterial< FontRect2D >( mtr );
			}
		}

		IFont::Glyph const &	Get (uint i) const
		{
			return fnt._GetGlyph( i, height );
		}
	};


/*
=================================================
	constructor
=================================================
*/
	VectorFont::VectorFont (const EngineSubSystemsRef ess, EResource::type resType) :
		IFont( resType, ess ), _fontFreeType(null),
		_baseLineTopMargin(0.0f), _heightForMinLevel(8)
	{
	}
	
/*
=================================================
	destructor
=================================================
*/
	VectorFont::~VectorFont ()
	{
		if ( ESS()->GetGraphicsEngine().IsNotNull() )
		{
			ESS()->GetGraphicsEngine()->GetFontLoader()->DestroyFontLevels( this );
		}
	}
	
/*
=================================================
	Draw
=================================================
*/
	void VectorFont::Draw (const FontBatch &params, const vec2 &pos, uni_c_string text, bool scissor)
	{
		rect const &	view = ESS()->GetGraphicsEngine()->GetViewport()->ViewRect();

		Draw( params, rect( pos.x, view.bottom, view.right, pos.y ), text, scissor );
	}
	
/*
=================================================
	Draw
=================================================
*/
	void VectorFont::Draw (const FontBatch &params, const rect &region, uni_c_string text, bool scissor)
	{
		BatchRenderPtr	br = ESS()->GetGraphicsEngine()->GetBatchRender();
		ViewportPtr		vp = ESS()->GetGraphicsEngine()->GetViewport();
		const float		k  = vp->ViewRect().GetSize().y * vp->GetResCorrectAspect().y * vp->Size().y;

		GlyphReader		reader( *this, k );

		IFont::_Draw( vp, br, reader, params, region, text, scissor );
	}
	
/*
=================================================
	Draw
=================================================
*/
	void VectorFont::Draw (const FontBatch &params, const vec2 &pos, const ext_string &text, bool scissor)
	{
		rect const &	view = ESS()->GetGraphicsEngine()->GetViewport()->ViewRect();

		Draw( params, rect( pos.x, view.bottom, view.right, pos.y ), text, scissor );
	}
	
/*
=================================================
	Draw
=================================================
*/
	void VectorFont::Draw (const FontBatch &params, const rect &region, const ext_string &text, bool scissor)
	{
		BatchRenderPtr	br = ESS()->GetGraphicsEngine()->GetBatchRender();
		ViewportPtr		vp = ESS()->GetGraphicsEngine()->GetViewport();
		const float		k  = vp->ViewRect().GetSize().y * vp->GetResCorrectAspect().y * vp->Size().y;

		GlyphReader		reader( *this, k );

		IFont::_Draw( vp, br, reader, params, region, text, scissor );
	}
	
/*
=================================================
	GetTextDimensions
=================================================
*/
	void VectorFont::GetTextDimensions (const FontBatch &params, uni_c_string text, OUT vec2 &dim, OUT usize &lineLength)
	{
		ViewportPtr		vp = ESS()->GetGraphicsEngine()->GetViewport();
		const float		k  = vp->ViewRect().GetSize().y * vp->GetResCorrectAspect().y * vp->Size().y;

		GlyphReader		reader( *this, k );

		IFont::_GetTextDimensions( vp, reader, params, text, dim, lineLength );
	}
	
/*
=================================================
	GetTextLines
=================================================
*/
	void VectorFont::GetFormattedText (const FontBatch &params, float maxLineWidth, const ext_string &text,
										const LocaleCharacters &localeChars, OUT ext_string &formattedText, OUT vec2 &dim)
	{
		ViewportPtr		vp = ESS()->GetGraphicsEngine()->GetViewport();
		const float		k  = vp->ViewRect().GetSize().y * vp->GetResCorrectAspect().y * vp->Size().y;

		GlyphReader		reader( *this, k );

		IFont::_GetFormattedText( vp, reader, maxLineWidth, params, text, localeChars, formattedText, dim );
	}

/*
=================================================
	SwapData
=================================================
*/
	bool VectorFont::SwapData (const ResourcePtr &res)
	{
		CHECK_ERR( Resource::SwapData( res ) );
		
		VectorFont *	font = res.ToPtr< VectorFont >();

		_swap( this->_baseLineTopMargin,	font->_baseLineTopMargin );
		_swap( this->_fontFreeType,			font->_fontFreeType );

		_levels.SwapMem( font->_levels );
		return true;
	}
		
/*
=================================================
	IsLoadedWithParams
=================================================
*/
	bool VectorFont::IsLoadedWithParams (const ResLoadParams &params) const
	{
		return Resource::IsLoadedWithParams( params );
	}
	
/*
=================================================
	IsValid
=================================================
*/
	bool VectorFont::IsValid () const
	{
		return _fontFreeType.IsNotNull();
	}

/*
=================================================
	New
=================================================
*/
	VectorFontPtr  VectorFont::New (const EngineSubSystemsRef ess)
	{
		return BaseObject::_New( new VectorFont( ess ) );
	}
	
/*
=================================================
	GetDefaultLoadParams
=================================================
*/
	VectorFont::VectorFontLoadParams  VectorFont::GetDefaultLoadParams ()
	{
		return VectorFontLoadParams();
	}
	
/*
=================================================
	_CalcBaseLineTopMargin
=================================================
*/
	inline float VectorFont::_CalcBaseLineTopMargin (uint height) const
	{
		return Round( height * _baseLineTopMargin ) / (float)height;
	}
	
/*
=================================================
	_CalcHeight
=================================================
*/
	inline uint VectorFont::_CalcHeight (float k, float scale) const
	{
		return Round<uint>( scale * k * 0.5f / _heightForMinLevel ) * _heightForMinLevel;
	}
	
/*
=================================================
	_LoadGlyph
=================================================
*/
	inline bool VectorFont::_LoadGlyph (uint8 symbol, uint height, Texture *&tex)
	{
		usize	idx = -1;

		if ( not _levels.FindIndex( height, idx ) )
		{
			CHECK_ERR( ESS()->GetGraphicsEngine()->GetFontLoader()->CreateLevel( this, height ) );

			idx = _levels.Add( height, Level() );
		}

		Level &	lvl = _levels[idx].second;

		if ( not lvl.glyphs[ symbol ].loaded )
		{
			CHECK_ERR( ESS()->GetGraphicsEngine()->GetFontLoader()->
							LoadGlyph( this, symbol, height, lvl.glyphs[symbol] ) );
		}

		tex = lvl.glyphs[ symbol ].texture;
		return true;
	}
	
/*
=================================================
	_GetGlyph
=================================================
*/
	inline const VectorFont::VecGlyph &  VectorFont::_GetGlyph (uint8 symbol, uint height) const
	{
		glyph_levels_t::iterator	res;

		if ( _levels.Find( height, res ) )
		{
			VecGlyph const & glyph = res->second.glyphs[ symbol ];
			
			ASSERT( glyph.loaded );
			return glyph;
		}

		static const VecGlyph	tmp;
		return tmp;
	}
	
/*
=================================================
	GetScaleForHeight
=================================================
*/
	float VectorFont::GetScaleForHeight (uint heightInPixels) const
	{
		ViewportPtr	vp = ESS()->GetGraphicsEngine()->GetViewport();

		return IFont::_CalcDefaultScale( vp, heightInPixels );
	}
	
/*
=================================================
	PreloadSymbols
=================================================
*/
	bool VectorFont::PreloadSymbols (uint height, char first, char last)
	{
		Texture *	tex = null;

		for (uint i = (uint8)first; i <= (uint8)last; ++i)
		{
			CHECK_ERR( _LoadGlyph( (uint8) i, height, tex ) );
		}
		return true;
	}
	
/*
=================================================
	GetTexturesForLevel
=================================================
*/
	bool VectorFont::GetTexturesForLevel (uint height, array<TexturePtr> &textures)
	{
		glyph_levels_t::iterator	res;

		textures.ClearMem();

		set<TexturePtr>	unique_tex;

		MemMover::Swap( unique_tex, textures );

		if ( _levels.Find( height, res ) )
		{
			FOR( i, res->second.glyphs )
			{
				if ( res->second.glyphs[i].texture.IsNull() )
					continue;

				unique_tex.AddOrReplace( res->second.glyphs[i].texture.ptr() );
			}
			
			MemMover::Swap( unique_tex, textures );
			return true;
		}
		
		MemMover::Swap( unique_tex, textures );
		return false;
	}


}	// Graphics
}	// Engine